use actix_web::{get, http::header::ContentType, web, App, HttpResponse, HttpServer, Responder};
use image::ImageFormat;
use serde::Deserialize;

use crate::{
    screenshot_local::{self, ScreenTarget},
    types::TResult,
};

/// App 全局状态
struct AppState {
    /// 默认截取的屏幕
    default_screen: ScreenTarget,
    /// 默认的图片格式
    default_format: ImageFormat,
}

/// 远程截图请求参数
#[derive(Deserialize)]
struct Params {
    /// 目标屏幕
    #[serde(default)]
    screen: String,
    /// 图片格式
    #[serde(default)]
    format: String,
}

impl Params {
    /// 格式化目标屏幕
    pub fn parse_screen(&self, default: ScreenTarget) -> ScreenTarget {
        match self.screen.to_uppercase().as_str() {
            "PRIMARY" => ScreenTarget::Primary,
            "ALL" => ScreenTarget::All,
            _ => default,
        }
    }

    /// 格式化类型，暂时仅支持 png 和 jpg
    pub fn parse_format(&self, default: ImageFormat) -> (ImageFormat, ContentType) {
        match self.format.to_uppercase().as_str() {
            "PNG" => (ImageFormat::Png, ContentType::png()),
            "JPG" | "JPEG" => (ImageFormat::Jpeg, ContentType::jpeg()),
            _ => (
                default,
                match default {
                    ImageFormat::Png => ContentType::png(),
                    ImageFormat::Jpeg => ContentType::jpeg(),
                    _ => ContentType::form_url_encoded(),
                },
            ),
        }
    }
}

/// 用于服务检查
#[get("/")]
async fn index() -> impl Responder {
    "everything is ok."
}

/// 截屏
#[get("/screenshot")]
async fn screenshot(query: web::Query<Params>, data: web::Data<AppState>) -> impl Responder {
    let (formatter, content_type) = query.parse_format(data.default_format);
    match screenshot_local::screenshot_bytes(query.parse_screen(data.default_screen), formatter) {
        Ok(img_bytes) => HttpResponse::Ok()
            .insert_header(content_type)
            .body(img_bytes),
        _ => HttpResponse::InternalServerError()
            .insert_header(content_type)
            .body("截图失败"),
    }
}

/// 启动服务，单线程
pub async fn run(
    addr: String,
    port: u16,
    default_screen: ScreenTarget,
    default_format: ImageFormat,
) -> TResult<()> {
    let err = HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(AppState {
                default_format,
                default_screen,
            }))
            .service(index)
            .service(screenshot)
    })
    .bind((addr, port))?
    .workers(1)
    .run()
    .await;
    match err {
        Ok(_) => Ok(()),
        Err(err) => Err(Box::new(err)),
    }
}
